#include <windows.h>
#include <stdlib.h>
#include <string.h>
#include <tchar.h>
#include <string>
#include <sstream>

/// <summary>
/// ListDefaultParamStruct is passed to ListSetDefaultParams to inform the plugin about the current 
/// plugin interface version and ini file location.
/// </summary>
typedef struct
{
   /// <summary>
   /// The size of the structure, in bytes. Later revisions of the plugin interface may add more structure members, 
   /// and will adjust this size field accordingly.
   /// </summary>
   int size;

   /// <summary>
   /// Low value of plugin interface version. This is the value after the comma, multiplied by 100! 
   /// Example. For plugin interface version 1.3, the low DWORD is 30 and the high DWORD is 1.
   /// </summary>
   DWORD PluginInterfaceVersionLow;

   /// <summary>
   /// The plugin interface version hiHigh value of plugin interface version.
   /// </summary>
   DWORD PluginInterfaceVersionHi;

   /// <summary>
   /// Suggested location+name of the ini file where the plugin could store its data. 
   /// This is a fully qualified path+file name, and will be in the same directory as the wincmd.ini. 
   /// It's recommended to store the plugin data in this file or at least in this directory, because the plugin 
   /// directory or the Windows directory may not be writable!
   /// </summary>
   char DefaultIniName[MAX_PATH];
} ListDefaultParamStruct;


// Function pointer for our imported function
typedef void (APIENTRY *f_ListSetDefaultParams)(ListDefaultParamStruct*);
f_ListSetDefaultParams ListSetDefaultParams = NULL;

typedef HWND(APIENTRY *f_ListLoad)(HWND, char*, int);
f_ListLoad ListLoad = NULL;

typedef void (APIENTRY *f_ListCloseWindow)(HWND);
f_ListCloseWindow ListCloseWindow = NULL;

HWND pluginHandle = NULL;

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
   std::wstringstream msg;

   switch (message)
   {
      case WM_DESTROY:
         PostQuitMessage(0);
         break;
      case WM_SIZE:
         if (NULL != pluginHandle)
         {
            int width = LOWORD(lParam);
            int height = HIWORD(lParam);
            MoveWindow(pluginHandle, 0, 0, width, height, true);
         }
         break;
      case WM_KEYDOWN:
         if (VK_ESCAPE == wParam)
         {
            if (NULL != ::ListCloseWindow)
            {
               ::ListCloseWindow(pluginHandle);
               PostQuitMessage(0);
            }
         }

         msg << L"App: WM_KEYDOWN - " << wParam << "\n";
         OutputDebugString(msg.str().c_str());
         break;
      default:
         return DefWindowProc(hWnd, message, wParam, lParam);
         break;
   }

   return 0;
}

HWND InitMainWindow(HINSTANCE hInstance, int nCmdShow, LPCWSTR szTitle)
{
   static TCHAR szWindowClass[] = _T("listertestapp");

   WNDCLASSEX wcex;

   wcex.cbSize = sizeof(WNDCLASSEX);
   wcex.style = CS_HREDRAW | CS_VREDRAW;
   wcex.lpfnWndProc = WndProc;
   wcex.cbClsExtra = 0;
   wcex.cbWndExtra = 0;
   wcex.hInstance = hInstance;
   wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_APPLICATION));
   wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
   wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
   wcex.lpszMenuName = NULL;
   wcex.lpszClassName = szWindowClass;
   wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_APPLICATION));

   if (!RegisterClassEx(&wcex))
   {
      MessageBox(NULL, _T("Call to RegisterClassEx failed!"), szTitle, NULL);

      return NULL;
   }

   // The parameters to CreateWindow explained:
   // szWindowClass: the name of the application
   // szTitle: the text that appears in the title bar
   // WS_OVERLAPPEDWINDOW: the type of window to create
   // CW_USEDEFAULT, CW_USEDEFAULT: initial position (x, y)
   // 500, 100: initial size (width, length)
   // NULL: the parent of this window
   // NULL: this application does not have a menu bar
   // hInstance: the first parameter from WinMain
   // NULL: not used in this application
   HWND hWnd = CreateWindow(
      szWindowClass,
      szTitle,
      WS_OVERLAPPEDWINDOW,
      CW_USEDEFAULT, CW_USEDEFAULT,
      1000, 500,
      NULL,
      NULL,
      hInstance,
      NULL
      );

   if (!hWnd)
   {
      MessageBox(NULL, _T("Call to CreateWindow failed!"), szTitle, NULL);

      return NULL;
   }

   // The parameters to ShowWindow explained:
   // hWnd: the value returned from CreateWindow
   // nCmdShow: the fourth parameter from WinMain
   ShowWindow(hWnd, nCmdShow);
   UpdateWindow(hWnd);

   return hWnd;
}

bool LoadPlugin()
{
   HINSTANCE dllInstance = LoadLibrary(_T("TotalDcmLister.wlx"));
   if (!dllInstance)
   {
      return false;
   }

   ::ListSetDefaultParams = (f_ListSetDefaultParams)GetProcAddress(dllInstance, "ListSetDefaultParams");
   if (NULL == ::ListSetDefaultParams)
   {
      return false;
   }

   ::ListLoad = (f_ListLoad)GetProcAddress(dllInstance, "ListLoad");
   if (NULL == ::ListLoad)
   {
      return false;
   }

   ::ListCloseWindow = (f_ListCloseWindow)GetProcAddress(dllInstance, "ListCloseWindow");
   if (NULL == ::ListCloseWindow)
   {
      return false;
   }

   return true;
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
   static TCHAR szTitle[] = _T("Lister Test Application");

   HWND hWnd = InitMainWindow(hInstance, nCmdShow, szTitle);

   if (false == LoadPlugin())
   {
      MessageBox(NULL, _T("Failed to load plugin DLL!"), szTitle, NULL);
      return 1;
   }

   ListDefaultParamStruct dps;
   dps.PluginInterfaceVersionHi = 1;
   dps.PluginInterfaceVersionLow = 30;
   
   char cwd[MAX_PATH];
   GetCurrentDirectoryA(MAX_PATH, cwd);

   std::string cwdAsString(cwd);
   std::string path = cwdAsString + "\\TotalDcmListerPlugin.default.settings.xml";

   int count = path._Copy_s(dps.DefaultIniName, MAX_PATH, path.length());
   dps.DefaultIniName[count] = '\0';

   ::ListSetDefaultParams(&dps);

   pluginHandle = ::ListLoad(hWnd, "test-EPDF.dcm", nCmdShow);

   // Main message loop:
   MSG msg;
   while (GetMessage(&msg, NULL, 0, 0))
   {
      TranslateMessage(&msg);
      DispatchMessage(&msg);
   }

   return (int)msg.wParam;
}